(ns clojure-test-customer-datastruct.mydefrecord)

;; defrecord是deftype的特例
;; 增加了额外特性
;; 值语义
;; 实现了关系型数据结构的接口
;; 元数据的支持
;; 对于clojure reader的支持
;; 构造函数

;; 记录类型不可变
;; 类型所有字段相等 那么类型相等
(defrecord Point [x y])
(= (Point. 3 4) (Point. 3 4))
(= 3 3N)
(= (Point. 3N 4N) (Point. 3 4))

;; 自动提供正确而一致的Object.equals与Object.hashCode实现


;; clojure.lang.Associative 适用map操作函数
;; 通过关键字来访问
(:x (Point. 3 4))
(:z (Point. 3 4) 0)
(map :x [(Point. 3 4)
         (Point. 5 6)
         (Point. 7 8)])
;; 记录类型可以传给任何需要java map的函数/方法来适用 java.util.Map接口
;; 运行时添加新字段

(assoc (Point. 3 4) :z 5)
;; 记录在运行时可以添加新字段
(let [p (assoc (Point. 3 4) :z 5)]
  (dissoc p :x))
;; 删除一个域定义的属性会破坏记录
(let [p (assoc (Point. 3 4) :z 5)]
  (dissoc p :z))
;; 只要维持预定义的记录 就维持了整个记录
;; 运行时额外添加的字段是被保存在单独的clojure hashmap里面

;; 通过meta获取记录的元数据信息
;; 通过with-meta设置记录的元数据信息

;; 从记录的文本表示读入一条记录
;; 意义在于序列化的时候非常方便
(pr-str (assoc (Point. 3 5) :a [:b :c]))
(= (read-string *1)
   (assoc (Point. 3 5) :a [:b :c]))

;; 附加构造函数
;; 扩展字段与元数据的支持
(=
  (Point. 3 4 {:foo :bar} {:z 5})
  (-> (assoc (Point. 3 4) :z 5)
      (with-meta {:foo :bar})))

;; 不到万不得已 不要暴露构造函数
;; 最大化api的稳定性
;; 工厂函数内部添加一些逻辑
;; 隐式创建工厂函数->RecordName
(->Point 3 4)
;; 隐式创建工厂函数map->Point
(map->Point {:x 3 :y 4 :z 5})
;; 跟高阶函数进行交互的时候有用


(apply ->Point [5 6])
;; apply函数会把向量中的参数序列应用到构造函数

(map (partial apply ->Point) [[5 6] [7 8] [9 10]])
;; map是遍历序列 跟apply有区别

;; 默认提供的工厂函数没有校验逻辑
(defn log-point
  [x]
  {:pre [(pos? x)]}
  (Point/create {:x x :y (Math/log x)}))

;; cljoure hashmap 优于 记录使用
;; 基于类型的多态 跟java互操作
;; 普通map和记录永远不相等